<template>
  <div v-click-outside:[capture]="handleClose" :class="classes">
    <div ref="reference" :class="wrapClasses" @click="toggleVisible">
      <input :name="name" :value="currentValue" type="hidden">
      <Icon :type="arrowType" :custom="customArrowType" :size="arrowSize" :class="arrowClasses"></Icon>
      <div ref="input" :tabindex="itemDisabled ? undefined : 0" :class="inputClasses" @keydown.tab="onTab"
        @keydown.esc="onEscape" @keydown.up="onArrow" @keydown.down="onArrow">
        <div :class="[prefixCls + '-color']">
          <div v-show="value === '' && !visible" :class="[prefixCls + '-color-empty']">
            <i :class="[iconPrefixCls, iconPrefixCls + '-ios-close']"></i>
          </div>
          <div v-show="value || visible" :style="displayedColorStyle"></div>
        </div>
      </div>
    </div>
    <transition name="transition-drop">
      <Drop v-show="visible" ref="drop" v-transfer-dom :placement="placement" :data-transfer="transfer"
        :transfer="transfer" :class="dropClasses" :eventsEnabled="eventsEnabled">
        <transition name="fade">
          <div v-if="visible" :class="[prefixCls + '-picker']">
            <div :class="[prefixCls + '-picker-wrapper']">
              <div :class="[prefixCls + '-picker-panel']">
                <Saturation ref="saturation" v-model="saturationColors" :focused="visible" @change="childChange"
                  @keydown.native.tab="handleFirstTab"></Saturation>
              </div>
              <div v-if="hue" :class="[prefixCls + '-picker-hue-slider']">
                <Hue v-model="saturationColors" @change="childChange"></Hue>
              </div>
              <div v-if="alpha" :class="[prefixCls + '-picker-alpha-slider']">
                <Alpha v-model="saturationColors" @change="childChange"></Alpha>
              </div>
              <recommend-colors v-if="colors.length" :list="colors" :class="[prefixCls + '-picker-colors']"
                @picker-color="handleSelectColor"></recommend-colors>
              <recommend-colors v-if="!colors.length && recommend" :list="recommendedColor"
                :class="[prefixCls + '-picker-colors']" @picker-color="handleSelectColor"></recommend-colors>
            </div>
            <div :class="[prefixCls + '-confirm']">
              <span :class="confirmColorClasses">
                <template v-if="editable">
                  <i-input ref="editColorInput" :value="formatColor" size="small" @on-enter="handleEditColor"
                    @on-blur="handleEditColor"></i-input>
                </template>
                <template v-else>{{ formatColor }}</template>
              </span>
              <i-button ref="clear" :tabindex="0" size="small" :style="getButtonStyle" @click.native="handleClear"
                @keydown.enter="handleClear" @keydown.native.esc="closer">{{ t('i.datepicker.clear') }}</i-button>
              <i-button ref="ok" :style="getButtonStyle" :tabindex="0" size="small" type="primary"
                @click.native="handleSuccess" @keydown.native.tab="handleLastTab" @keydown.enter="handleSuccess"
                @keydown.native.esc="closer">{{ t('i.datepicker.ok') }}</i-button>
            </div>
          </div>
        </transition>
      </Drop>
    </transition>
  </div>
</template>

<script>
import tinycolor from 'tinycolor2';
import { directive as clickOutside } from '../../directives/v-click-outside-x';
import TransferDom from '../../directives/transfer-dom';
import Drop from '../../components/select/dropdown.vue';
import RecommendColors from './recommend-colors.vue';
import Saturation from './saturation.vue';
import Hue from './hue.vue';
import Alpha from './alpha.vue';
import iInput from '../input/input.vue';
import iButton from '../button/button.vue';
import Icon from '../icon/icon.vue';
import Locale from '../../mixins/locale';
import { oneOf } from '../../utils/assist';
import Emitter from '../../mixins/emitter';
import mixinsForm from '../../mixins/form';
import Prefixes from './prefixMixin';
import { changeColor, toRGBAString } from './utils';

export default {
  name: 'ColorPicker',

  components: { Drop, RecommendColors, Saturation, Hue, Alpha, iInput, iButton, Icon },

  directives: { clickOutside, TransferDom },

  mixins: [Emitter, Locale, Prefixes, mixinsForm],

  props: {
    value: {
      type: String,
      default: undefined
    },
    hue: {
      type: Boolean,
      default: true
    },
    alpha: {
      type: Boolean,
      default: false
    },
    recommend: {
      type: Boolean,
      default: false
    },
    format: {
      type: String,
      validator(value) {
        return oneOf(value, ['hsl', 'hsv', 'hex', 'rgb']);
      },
      default: undefined
    },
    colors: {
      type: Array,
      default() {
        return [];
      }
    },
    disabled: {
      type: Boolean,
      default: false
    },
    size: {
      validator(value) {
        return oneOf(value, ['small', 'large', 'default']);
      },
      default() {
        return !this.$IVIEW || this.$IVIEW.size === '' ? 'default' : this.$IVIEW.size;
      }
    },
    hideDropDown: {
      type: Boolean,
      default: false
    },
    placement: {
      type: String,
      validator(value) {
        return oneOf(value, ['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end', 'right', 'right-start', 'right-end']);
      },
      default: 'bottom'
    },
    transfer: {
      type: Boolean,
      default() {
        return !this.$IVIEW || this.$IVIEW.transfer === '' ? false : this.$IVIEW.transfer;
      }
    },
    name: {
      type: String,
      default: undefined
    },
    editable: {
      type: Boolean,
      default: true
    },
    // 4.0.0
    capture: {
      type: Boolean,
      default() {
        return !this.$IVIEW ? true : this.$IVIEW.capture;
      }
    },
    transferClassName: {
      type: String
    },
    // 4.6.0
    eventsEnabled: {
      type: Boolean,
      default: false
    }
  },

  data() {
    return {
      val: changeColor(this.value || ''),
      currentValue: this.value || '',
      dragging: false,
      visible: false,
      recommendedColor: ['#2d8cf0', '#19be6b', '#ff9900', '#ed4014', '#00b5ff', '#19c919', '#f9e31c', '#ea1a1a', '#9b1dea', '#00c2b1', '#ac7a33', '#1d35ea', '#8bc34a', '#f16b62', '#ea4ca3', '#0d94aa', '#febd79', '#5d4037', '#00bcd4', '#f06292', '#cddc39', '#607d8b', '#000000', '#ffffff']
    };
  },

  mounted() {
    this.$on('on-escape-keydown', this.closer);
    this.$on('on-dragging', this.setDragging);
  },

  methods: {
    setDragging(value) {
      this.dragging = value;
    },
    handleClose(event) {
      if (this.visible) {
        if (this.dragging || event.type === 'mousedown') {
          if (this.$refs.editColorInput && event.target !== this.$refs.editColorInput.$el.querySelector('input')) {
            event.preventDefault(); // 修复 ColorPicker 在和 Select 等组件共同使用时，颜色输入框无法激活的问题。
          }
          return;
        }

        if (this.transfer) {
          const { $el } = this.$refs.drop;
          if ($el === event.target || $el.contains(event.target)) {
            return;
          }
        }

        this.closer(event);
        return;
      }

      this.visible = false;
    },
    toggleVisible() {
      if (this.itemDisabled) {
        return;
      }

      this.visible = !this.visible;
      this.$refs.input.focus();
    },
    childChange(data) {
      this.colorChange(data);
    },
    colorChange(data, oldHue) {
      this.oldHue = this.saturationColors.hsl.h;
      this.saturationColors = changeColor(data, oldHue || this.oldHue);
    },
    closer(event) {
      if (event) {
        event.preventDefault();
        event.stopPropagation();
      }

      this.visible = false;
      this.$refs.input.focus();
    },
    handleButtons(event, value) {
      this.currentValue = value;
      this.$emit('input', value);
      this.$emit('on-change', value);
      this.dispatch('FormItem', 'on-form-change', value);
      this.closer(event);
    },
    handleSuccess(event) {
      this.handleButtons(event, this.formatColor);
      this.$emit('on-pick-success');
    },
    handleClear(event) {
      this.handleButtons(event, '');
      this.$emit('on-pick-clear');
    },
    handleSelectColor(color) {
      this.val = changeColor(color);
      this.$emit('on-active-change', this.formatColor);
    },
    handleEditColor(event) {
      const value = event.target.value;
      this.handleSelectColor(value);
    },
    handleFirstTab(event) {
      if (event.shiftKey) {
        event.preventDefault();
        event.stopPropagation();
        this.$refs.ok.$el.focus();
      }
    },
    handleLastTab(event) {
      if (!event.shiftKey) {
        event.preventDefault();
        event.stopPropagation();
        this.$refs.saturation.$el.focus();
      }
    },
    onTab(event) {
      if (this.visible) {
        event.preventDefault();
      }
    },
    onEscape(event) {
      if (this.visible) {
        this.closer(event);
      }
    },
    onArrow(event) {
      if (!this.visible) {
        event.preventDefault();
        event.stopPropagation();
        this.visible = true;
      }
    }
  },

  computed: {
    arrowClasses() {
      return [`${this.inputPrefixCls}-icon`, `${this.inputPrefixCls}-icon-normal`];
    },
    transition() {
      return oneOf(this.placement, ['bottom-start', 'bottom', 'bottom-end']) ? 'slide-up' : 'fade';
    },
    saturationColors: {
      get() {
        return this.val;
      },
      set(newVal) {
        this.val = newVal;
        this.$emit('on-active-change', this.formatColor);
      }
    },
    classes() {
      return [
        `${this.prefixCls}`,
        {
          [`${this.prefixCls}-transfer`]: this.transfer
        }
      ];
    },
    wrapClasses() {
      return [
        `${this.prefixCls}-rel`,
        `${this.prefixCls}-${this.size}`,
        `${this.inputPrefixCls}-wrapper`,
        `${this.inputPrefixCls}-wrapper-${this.size}`,
        {
          [`${this.prefixCls}-disabled`]: this.itemDisabled
        }
      ];
    },
    inputClasses() {
      return [
        `${this.prefixCls}-input`,
        `${this.inputPrefixCls}`,
        `${this.inputPrefixCls}-${this.size}`,
        {
          [`${this.prefixCls}-focused`]: this.visible,
          [`${this.prefixCls}-disabled`]: this.itemDisabled
        }
      ];
    },
    dropClasses() {
      return [
        `${this.transferPrefixCls}-no-max-height`,
        {
          [`${this.prefixCls}-transfer`]: this.transfer,
          [`${this.prefixCls}-hide-drop`]: this.hideDropDown,
          [this.transferClassName]: this.transferClassName
        }
      ];
    },
    displayedColorStyle() {
      return { backgroundColor: toRGBAString(this.visible ? this.saturationColors.rgba : tinycolor(this.value).toRgb()) };
    },
    formatColor() {
      const { format, saturationColors } = this;

      if (format) {
        if (format === 'hsl') {
          return tinycolor(saturationColors.hsl).toHslString();
        }

        if (format === 'hsv') {
          return tinycolor(saturationColors.hsv).toHsvString();
        }

        if (format === 'hex') {
          return saturationColors.hex;
        }

        if (format === 'rgb') {
          return toRGBAString(saturationColors.rgba);
        }
      } else if (this.alpha) {
        return toRGBAString(saturationColors.rgba);
      }

      return saturationColors.hex;
    },
    confirmColorClasses() {
      return [
        `${this.prefixCls}-confirm-color`,
        {
          [`${this.prefixCls}-confirm-color-editable`]: this.editable
        }
      ];
    },
    // 3.4.0, global setting customArrow 有值时，arrow 赋值空
    arrowType() {
      let type = 'ios-arrow-down';

      if (this.$IVIEW) {
        if (this.$IVIEW.colorPicker.customArrow) {
          type = '';
        } else if (this.$IVIEW.colorPicker.arrow) {
          type = this.$IVIEW.colorPicker.arrow;
        }
      }
      return type;
    },
    // 3.4.0, global setting
    customArrowType() {
      let type = '';

      if (this.$IVIEW) {
        if (this.$IVIEW.colorPicker.customArrow) {
          type = this.$IVIEW.colorPicker.customArrow;
        }
      }
      return type;
    },
    // 3.4.0, global setting
    arrowSize() {
      let size = '';

      if (this.$IVIEW) {
        if (this.$IVIEW.colorPicker.arrowSize) {
          size = this.$IVIEW.colorPicker.arrowSize;
        }
      }
      return size;
    },
    getButtonStyle() {
      // transfer为true时，按钮和input对不齐的问题，同时添加右边间隙
      if (this.transfer) {
        return 'margin-top: 4px;margin-right: 6px;';
      }
      return 'margin-right: 6px;';
    }
  },

  watch: {
    value(newVal) {
      this.val = changeColor(newVal || '');
    },
    visible(val) {
      this.val = changeColor(this.value || '');
      this.$refs.drop[val ? 'update' : 'destroy']();
      this.$emit('on-open-change', Boolean(val));
    }
  }
};
</script>
